by Gene Olafsen
In This Chapter
This chapter teaches you a number of ways to implement automation clientsoften called controllers. This discussion includes details about the IDispatch interface, as well as the various ways you can put automation servers to work in your application.
Any discussion regarding automation usually begins with a description of the IDispatch interface because it is the most recognizable aspect of automation development and its methods form a framework for presentation of automation topics. This is certainly the case for this chapter. However, back in the days when the only material available on the subject was Inside OLE by Kraig Brockschmidt, it sometimes became confusing whether the discussion centered on material that was implemented by the server or the controller. There is a relatively simple set of rules to remember when tackling IDispatch and its place in automation:
You can understand IDispatchs place in an automation server/controller system by knowing that the IDispatch methods are implemented by the OLE runtime and exposed by the server objects custom interface. Notice, too, that the base IUnknown methods are also available to an automation controller; however, the implementation of such methods falls under the jurisdiction of the server object developer and not the OLE runtime. Automation servers differ from other COM object server configurations in which the server exposes the interface methods directly to the client.
Normal or nonautomation COM server interfaces derive directly from the IUnknown interface. Automation servers, however, derive from the IDispatch interface. Automation has been around a while, and, as such, both MFC and ATL support itthis is the good news. The bad news is that automation has been around for a while. The earliest definitions of automation interfaces are grounded in a format known as Object Definition Language (ODL). Just as C was the predecessor to C++, so too does ODL predate IDL. Before Visual Studios wizards came along to generate a sizeable portion of an interfaces definition, the files were generated by hand and compiled with a command-line utility named MkTypLib. This program is notorious for outputting error messages that are cryptic at best. It turns out that although Visual Studios AppWizard and ClassWizard still generate ODL code, a command-line compatibility switch allows the more common MIDL compiler to process the syntax.
A review of IDL and ODL yields the following differences:
To illustrate the similarities and differences between the languages, I will define an interface with the following characteristics.
The interface whose name is Slang contains two methods and two properties. The first method, Pass2Short, passes two shorts to the server, whereas the second method, Retrieve2Long, retrieves two longs from the server. The property methods, PropShort and PropLong, put and get a single short and long respectively.
It might seem odd that I have decided to define interfaces with such primitive types, but as you will soon find out, automation is very specific regarding the datatypes it marshals between components. That is not to say that there is no way of passing Boolean and string arguments; it is just that there is a specific manner in which to declare such datatypes.
The IDL definition of the interface description appears in Listing 13.1 and is the result of an interface definition using the various helper dialogs that Microsoft provides for ATL object creation.
Listing 13.1 The IDL Definition of the Interface Description
import oaidl.idl;
import ocidl.idl;
[
object,
uuid(D5F6CF73-615F-11D2-B10B-0000861D2934),
dual,
helpstring(ISlang Interface),
pointer_default(unique)
]
interface ISlang : IDispatch
{
[id(1), helpstring(method Pass2Short)]
HRESULT Pass2Short(short nS1, short nS2);
[id(2), helpstring(method Retrieve2Long)]
HRESULT Retrieve2Long(long* nL1, long* nL2);
[propget, id(3), helpstring(property PropShort)]
HRESULT PropShort([out, retval] long *pVal);
[propput, id(3), helpstring(property PropShort)]
HRESULT PropShort([in] long newVal);
[propget, id(4), helpstring(property PropLong)]
HRESULT PropLong([out, retval] long *pVal);
[propput, id(4), helpstring(property PropLong)]
HRESULT PropLong([in] long newVal);
};
[
uuid(D5F6CF66-615F-11D2-B10B-0000861D2934),
version(1.0),
helpstring(IDLandATL 1.0 Type Library)
]
library IDLANDATLLib
{
importlib(stdole32.tlb);
importlib(stdole2.tlb);
[
uuid(D5F6CF74-615F-11D2-B10B-0000861D2934),
helpstring(Slang Class)
]
coclass Slang
{
[default] interface ISlang;
};
};
The first entries of any IDL file that is generated by ATL include import statements for oaidl.idl and ocidl.idl. The import statement is similar in function to the #include statement in C/C++, instructing the compiler to include datatypes that are defined in the imported IDL files. In this case, the interface definition relies upon an IDL definition for the IDispatch interface. This definition appears in the \include\OAIDL.IDL file of your Visual Studio installation. The interface derives from IUnknown, as all interfaces ultimately must.